﻿using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Mars.GpuInstance
{
    public static class InstancerEditorUtil
    {
        public static Matrix4x4 GetTransformOffset(Transform parent, Transform target)
        {
            Matrix4x4 transformOffset = Matrix4x4.identity;
            Transform currentTransform = target;
            while (currentTransform != parent)
            {
                if (currentTransform == null)
                {
                    break;
                }
                transformOffset = Matrix4x4.TRS(currentTransform.localPosition, currentTransform.localRotation, currentTransform.localScale) * transformOffset;
                currentTransform = currentTransform.parent;
            }

            return transformOffset;
        }

#if UNITY_EDITOR
        
        public static string GetUnSupportGPUInstanceShaderName(List<SkinMeshLodDataForBake> lodDatas)
        {
            string str = "";
            var dic = new HashSet<string>();
            foreach (var lodData in lodDatas)
            {
                for (int i = 0; i < lodData.renderDatas.Count; i++)
                {
                    var renderData = lodData.renderDatas[i];
                    var materials = renderData.meshRenderer.sharedMaterials;
                    for (int j = 0; j < materials.Length; j++)
                    {
                        if (!InstancerUtil.IsShaderInstanced(materials[j].shader))
                        {
                            dic.Add(materials[j].shader.name);
                        }
                    }
                }
            }

            foreach (var name in dic)
            {
                str += name + "\n";
            }

            return str;
        }

        public static bool IsSupportGPUInstance(SkinMeshLodDataForBake lodData)
        {
            for (int i = 0; i < lodData.renderDatas.Count; i++)
            {
                var renderData = lodData.renderDatas[i];
                var materials = renderData.meshRenderer.sharedMaterials;
                for (int j = 0; j < materials.Length; j++)
                {
                    if (!InstancerUtil.IsShaderInstanced(materials[j].shader))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        public static void CreateSupportGPUInstanceShader(SkinMeshLodDataForBake lodData)
        {
            Dictionary<string, Shader> shaderDic = new Dictionary<string, Shader>();
            Dictionary<string, List<Material>> matDic = new Dictionary<string, List<Material>>();
            for (int i = 0; i < lodData.renderDatas.Count; i++)
            {
                var renderData = lodData.renderDatas[i];
                var materials = renderData.meshRenderer.sharedMaterials;
                for (int j = 0; j < materials.Length; j++)
                {
                    var mat = materials[j];
                    if (InstancerUtil.IsShaderInstanced(mat.shader))
                    {
                        continue;
                    }

                    if (!shaderDic.ContainsKey(mat.shader.name))
                    {
                        shaderDic.Add(mat.shader.name, materials[j].shader);
                    }

                    if (!matDic.ContainsKey(mat.shader.name))
                    {
                        matDic.Add(mat.shader.name, new List<Material>());
                    }

                    matDic[mat.shader.name].Add(mat);
                }
            }

            foreach (var kv in shaderDic)
            {
                Shader shader = Shader.Find("GPUInstancer/" + kv.Key);

                if (!shader || !InstancerUtil.IsShaderInstanced(shader))
                {
                    shader = InstancerUtil.CreateInstancedShader(kv.Value, true);
                }

                if (shader != null && matDic.ContainsKey(kv.Key))
                {
                    var list = matDic[kv.Key];
                    foreach (var mat in list)
                    {
                        mat.shader = shader;
                    }
                }
            }
        }
        
        
        public static string GetUnSupportGPUInstanceShaderName(List<LodData> lodDatas)
        {
            string str = "";
            var dic = new HashSet<string>();
            foreach (var lodData in lodDatas)
            {
                for (int i = 0; i < lodData.renderDatas.Count; i++)
                {
                    var renderData = lodData.renderDatas[i];
                    var materials = renderData.meshRenderer.sharedMaterials;
                    for (int j = 0; j < materials.Length; j++)
                    {
                        if (!InstancerUtil.IsShaderInstanced(materials[j].shader))
                        {
                            dic.Add(materials[j].shader.name);
                        }
                    }
                }
            }

            foreach (var name in dic)
            {
                str += name + "\n";
            }

            return str;
        }

        public static bool IsSupportGPUInstance(LodData lodData)
        {
            for (int i = 0; i < lodData.renderDatas.Count; i++)
            {
                var renderData = lodData.renderDatas[i];
                var materials = renderData.meshRenderer.sharedMaterials;
                for (int j = 0; j < materials.Length; j++)
                {
                    if (!InstancerUtil.IsShaderInstanced(materials[j].shader))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        public static void CreateSupportGPUInstanceShader(LodData lodData)
        {
            Dictionary<string, Shader> shaderDic = new Dictionary<string, Shader>();
            Dictionary<string, List<Material>> matDic = new Dictionary<string, List<Material>>();
            for (int i = 0; i < lodData.renderDatas.Count; i++)
            {
                var renderData = lodData.renderDatas[i];
                var materials = renderData.meshRenderer.sharedMaterials;
                for (int j = 0; j < materials.Length; j++)
                {
                    var mat = materials[j];
                    if (InstancerUtil.IsShaderInstanced(mat.shader))
                    {
                        continue;
                    }

                    if (!shaderDic.ContainsKey(mat.shader.name))
                    {
                        shaderDic.Add(mat.shader.name, materials[j].shader);
                    }

                    if (!matDic.ContainsKey(mat.shader.name))
                    {
                        matDic.Add(mat.shader.name, new List<Material>());
                    }

                    matDic[mat.shader.name].Add(mat);
                }
            }

            foreach (var kv in shaderDic)
            {
                Shader shader = Shader.Find("GPUInstancer/" + kv.Key);

                if (!shader || !InstancerUtil.IsShaderInstanced(shader))
                {
                    shader = InstancerUtil.CreateInstancedShader(kv.Value, true);
                }

                if (shader != null && matDic.ContainsKey(kv.Key))
                {
                    var list = matDic[kv.Key];
                    foreach (var mat in list)
                    {
                        mat.shader = shader;
                    }
                }
            }
        }


        public static bool IsPrefab(GameObject go)
        {
            var isAnyPrefabInstanceRoot = PrefabUtility.IsAnyPrefabInstanceRoot(go);
            var originalSource = PrefabUtility.GetCorrespondingObjectFromOriginalSource(go);
            if (isAnyPrefabInstanceRoot && originalSource != null)
            {
                var path = UnityEditor.AssetDatabase.GetAssetPath(originalSource);
                if (string.IsNullOrEmpty(path) || path.EndsWith(".fbx") || path.EndsWith(".FBX"))
                {
                    return false;
                }
                return true;
            }
            return false;
        }
        
        public static string GetPrefabAssetPath(GameObject gameObject, bool shortPath = true)
        {
            string path = "";

            var prefabType = UnityEditor.PrefabUtility.GetPrefabType(gameObject);

            if (prefabType == PrefabType.Prefab)
            {
                // Project中的Prefab是Asset不是Instance
                if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(gameObject))
                {
                    // 预制体资源就是自身
                    path = UnityEditor.AssetDatabase.GetAssetPath(gameObject);
                }
            }
            else
            {
                // Project中的Prefab是Asset不是Instance
                if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(gameObject))
                {
                    // 预制体资源就是自身
                    path = UnityEditor.AssetDatabase.GetAssetPath(gameObject);
                }

                // Scene中的Prefab Instance是Instance不是Asset
                if (UnityEditor.PrefabUtility.IsPartOfPrefabInstance(gameObject))
                {
                    // 获取预制体资源
                    var prefabAsset = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
                    path = UnityEditor.AssetDatabase.GetAssetPath(prefabAsset);
                }

                // PrefabMode中的GameObject既不是Instance也不是Asset
                var prefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetPrefabStage(gameObject);
                if (prefabStage != null && (string.IsNullOrEmpty(path) || path.EndsWith(".fbx") || path.EndsWith(".FBX")))
                {
                    // 预制体资源：prefabAsset = prefabStage.prefabContentsRoot
                    path = prefabStage.assetPath;
                }
            }
           

            if (!string.IsNullOrEmpty(path))
            {
                if (shortPath)
                {
                    path = path.Replace("Assets/res/", "");
                }
                return path;
            }
            
            // 不是预制体
            return "";
        }
        
        
        public static string GetPrefabAssetName(GameObject gameObject)
        {
            string path = GetPrefabAssetPath(gameObject, false);

            if (!string.IsNullOrEmpty(path))
            {
                var rootName = Path.GetFileNameWithoutExtension(path);
                var name = gameObject.name;
                if (name == rootName)
                    return name;
                
                var stack = new List<string>() { };
                for (var t = gameObject.transform; name != rootName && t; t = t.parent)
                {
                    name = t.name;
                    stack.Add(name);
                }

                if (name == rootName)
                {
                    var sb = new StringBuilder();
                    for (var i = stack.Count - 1; i > 0; --i)
                    {
                        sb.Append(stack[i]);
                        sb.Append('/');
                    }

                    sb.Append(stack[0]);
                    return sb.ToString();
                }

            }
            // 不是预制体
            return "";
        }
        
#endif
    }
}
